package com.huxi.lang.validation.predicate;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;

public abstract class Group implements VPredicate {

	public abstract Group add(VPredicate predicate, boolean and);

	public Group add(VPredicate predicate) {
		add(predicate, true);
		return this;
	}

	public Group add(VPredicate predicate, VPredicate... others) {
		add(predicate);
		return add(others);
	}

	public Group add(VPredicate[] predicates) {
		if (predicates != null) {
			for (VPredicate vp : predicates) {
				add(vp, true);
			}
		}
		return this;
	}

	public Group add(Collection<? extends VPredicate> predicates) {
		if (predicates != null) {
			for (VPredicate vp : predicates) {
				add(vp, true);
			}
		}
		return this;
	}

	public static Group and(VPredicate vp1, VPredicate vp2) {
		Objects.requireNonNull(vp1);
		Objects.requireNonNull(vp2);
		AndGroup rs = new AndGroup();
		rs.add(vp1);
		rs.add(vp2);
		return rs;
	}

	public static Group or(VPredicate vp1, VPredicate vp2) {
		Objects.requireNonNull(vp1);
		Objects.requireNonNull(vp2);
		OrGroup rs = new OrGroup();
		rs.add(vp2);
		return rs;
	}

	public static class AndGroup extends Group {
		private List<VPredicate> predicates;

		AndGroup(List<VPredicate> predicates) {
			this.predicates = Objects.requireNonNull(predicates);
		}

		AndGroup() {
			this.predicates = new ArrayList<>();
		}

		@Override
		public AndGroup add(VPredicate predicate, boolean and) {
			if (!(predicate == null || predicate == this || predicates.contains(predicate))) {
				predicates.add(predicate);
			}
			return this;
		}

		@Override
		public int size() {
			return predicates.size();
		}

		@Override
		public AndGroup immutable(boolean copy) {
			if (copy) {
				this.predicates = Collections.unmodifiableList(new ArrayList<>(predicates));
			} else {
				this.predicates = Collections.unmodifiableList(predicates);
			}
			return this;
		}

		@Override
		public boolean test(Object t, boolean valueIfNull, boolean valueIfIllegalType) {
			for (VPredicate vp : predicates) {
				if (!vp.test(t, valueIfNull, valueIfIllegalType)) {
					return false;
				}
			}
			return true;
		}
	}

	public static class OrGroup extends Group {
		private List<VPredicate> predicates;

		OrGroup(List<VPredicate> predicates) {
			this.predicates = Objects.requireNonNull(predicates);
		}

		OrGroup() {
			this.predicates = new ArrayList<>();
		}

		@Override
		public OrGroup add(VPredicate predicate, boolean and) {
			if (!(predicate == null || predicate == this || predicates.contains(predicate))) {
				predicates.add(predicate);
			}
			return this;
		}

		@Override
		public int size() {
			return predicates.size();
		}

		@Override
		public OrGroup immutable(boolean copy) {
			if (copy) {
				this.predicates = Collections.unmodifiableList(new ArrayList<>(predicates));
			} else {
				this.predicates = Collections.unmodifiableList(predicates);
			}
			return this;
		}

		@Override
		public boolean test(Object t, boolean valueIfNull, boolean valueIfIllegalType) {
			if (predicates.size() != 0) {
				for (VPredicate vp : predicates) {
					if (vp.test(t, valueIfNull, valueIfIllegalType)) {
						return true;
					}
				}
			}
			return true;
		}
	}

	static class VItem {
		private boolean and;
		private VPredicate vp;

		VItem(boolean and, VPredicate predicate) {
			this.and = and;
			this.vp = predicate;
		}
	}

	public static class AndOrGroup extends Group {

		private List<VItem> predicates;

		AndOrGroup(List<VItem> predicates) {
			this.predicates = Objects.requireNonNull(predicates);
		}

		public AndOrGroup() {
			this.predicates = new ArrayList<>();
		}

		@Override
		public AndOrGroup add(VPredicate predicate, boolean and) {
			if (!(predicate == null || predicate == this)) {
				for (VItem vi : predicates) {
					if (vi.vp.equals(predicate)) {
						return this;
					}
				}
				predicates.add(new VItem(and, predicate));
			}
			return this;
		}

		@Override
		public int size() {
			return predicates.size();
		}

		@Override
		public AndOrGroup immutable(boolean copy) {
			if (copy) {
				this.predicates = Collections.unmodifiableList(new ArrayList<>(predicates));
			} else {
				this.predicates = Collections.unmodifiableList(predicates);
			}
			return this;
		}

		@Override
		public boolean test(Object t, boolean valueIfNull, boolean valueIfIllegalType) {
			boolean st = true;
			if (predicates.size() != 0) {
				Iterator<VItem> itor = predicates.iterator();
				VItem vi = itor.next();
				st = vi.vp.test(itor, valueIfNull, valueIfIllegalType);
				while (itor.hasNext()) {
					vi = itor.next();
					if (vi.and) {
						if (!st) {
							return false;
						}
						st = vi.vp.test(itor, valueIfNull, valueIfIllegalType);
					} else {
						if (st) {
							return true;
						}
						st = vi.vp.test(itor, valueIfNull, valueIfIllegalType);
					}
				}
			}
			return st;
		}

	}

}
